package cn.xjfcloud.onedriveserver.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.druid.support.logging.Log;

import cn.xjfcloud.onedriveserver.pojo.dto.OnedriveFileInfo;
import cn.xjfcloud.onedriveserver.pojo.dto.UploadInfo;
import cn.xjfcloud.onedriveserver.service.UploadFileService;

public class UploadFileThread extends Thread{ 
	
	private static final ThreadLocal<Date> dateThreadLocal = new ThreadLocal<Date>() {
		@Override
		protected Date initialValue() {
			return new Date();
		};
	};
	
	private final static Logger LOG = LoggerFactory.getLogger(UploadFileThread.class);
	private UploadInfo uploadInfo;
	private UploadFileService uploadFileService;
	private String path;
	public UploadFileThread(UploadInfo uploadInfo, UploadFileService uploadFileService, String path) {
		this.uploadInfo = uploadInfo;
		this.uploadFileService = uploadFileService;
		this.path = path;
	}
	
	@Override
	public void run() {
		File file = new File(path);
		Long total = file.length();
		String uuid = UUID.randomUUID().toString();
		String uploadId = uploadInfo.getUploadId();
		UploadFileUtil.addRunningThread(uploadInfo.getUploadId(), uuid);
		try (FileInputStream fileInputStream  = new FileInputStream(file)){
			uploadInfo.setTotal(total);
			uploadFileService.updateUploadInfo(uploadInfo);
			Long offset = uploadFileService.getOffset(uploadInfo.getUploadId());
			// If your app splits a file into multiple byte ranges, the size of each byte
			// range MUST be a multiple of 320 KiB (327,680 bytes). Using a fragment size
			// that does not divide evenly by 320 KiB will result in errors committing some
			// files.
			// 微软建议大小为320K每一个片段
			Integer uplaodPartCount = uploadFileService.getUploadPartCount();
			Integer partLength = 327680 * uplaodPartCount;
			//初始化错误次数
			Integer errCount = 0;
			byte[] b = new byte[100];
			int len = 0;
			//片段上传
			while ((len = fileInputStream.read(b)) != -1) {
				if(!UploadFileUtil.getUUID(uploadId).equals(uuid)) {
					LOG.info("取消了上传任务，upload_id为"  + uploadInfo.getUploadId());
					break;
				}
				Map<String, String> resultMap = new HashMap<String, String>();
				try {
					resultMap = HttpUtil.uploadFileRange(b, uploadInfo.getUploadSession(), offset , total);
				}catch (Exception e) {
					errCount ++;
					//超过重试次数
					if(errCount > 10) {
						throw new IOException(uploadInfo.getUploadId() +  "upload fail");
					}else {
						continue;
					}
				}
				//重置错误次数
				errCount = 0;
				switch (resultMap.get("code")) {
				case "202":
					//片段上传完成
					Long newOffset = offset + len;
					Date now = new Date();
					Date old = dateThreadLocal.get();
					//LOG.info("old  " + String.valueOf(old.getTime()));
					Long s =  (now.getTime() - old.getTime())/1000;
					if(s > 10) {
						dateThreadLocal.set(now);
						uploadFileService.updateOffset(uploadInfo.getUploadId(), newOffset);
					}
					//判断还剩下的片段大小
					if(total - newOffset > (long)partLength) {
						b = new byte[partLength];
					}else {
						b = new byte[(int)(total - newOffset)];
					}
					offset = newOffset;
					break;
				case "201":
					//所有上传成功
					LOG.info(uploadInfo.getUploadId() + " : upload success");
					uploadFileService.updateOffset(uploadInfo.getUploadId(), total-1L);
					//保存进文件信息
					OnedriveFileInfo onedriveFileInfo = uploadFileService.getOnedriveFileInfoByUploadId(uploadId);
					onedriveFileInfo.setFileSize(total);
					uploadFileService.updateOnedriveFileInfo(onedriveFileInfo);
					//从map中删除该线程信息
					UploadFileUtil.removeUUID(uploadId);
					break;
				case "416":
					newOffset = offset + len;
					if(total - newOffset > (long)partLength) {
						b = new byte[partLength];
					}else {
						b = new byte[(int)(total - newOffset)];
					}
					//LOG.info("file offset is " + offset  + " has upload");
					offset = newOffset;
					continue;
				default:
					LOG.error(resultMap.toString());
					throw new IOException(uploadInfo.getUploadId() +  "upload fail, code " + resultMap.get("code"));
				}//switch --- end
			}//while --- end
		} catch (IOException e) {
			LOG.error("UploadFileThread-->run-->", e);
			return;
		}
	}
}
